﻿using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Nakio.Framework.Exceptions;
using Nakio.Framework.Interfaces;

namespace Nakio.Framework.Communication
{
    /// <summary>
    /// Command base class, all commands must inherit this class to work properly
    /// </summary>
    [Serializable]
    public abstract class CommandBase
    {       
        [NonSerialized] protected IClientCallback _clientCallback;
        [NonSerialized] protected EncryptToken _encryptToken;

        /// <summary>
        /// The execution of the command
        /// </summary>
        /*public virtual void Execute()
        {
            throw new NotImplementedException("This method is not implemented");
        }*/

        /// <summary>
        /// The execution of the command
        /// </summary>
        public abstract object Execute();

        [OnDeserialized]
        private void CommandBaseOnDeserialized(StreamingContext ctxt)
        {
            var serializeContext = (SerializeContext)ctxt.Context;
            _clientCallback = serializeContext.ClientCallback;
            _encryptToken = serializeContext.EncryptToken;

            AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => CurrentDomain_AssemblyResolve(this, e);
        }

        private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly assembly in assemblies)
            {
                if (assembly.FullName == args.Name)
                {
                    return assembly;
                }
            }
            var command = new MissingAssemblyCommand(args.Name);
            var encryptedCommand = new EncryptedCommand(_encryptToken, command);
            var assemblyBytes = (byte[])_clientCallback.ExecuteClientReturnCommand(encryptedCommand);

            Assembly.Load(assemblyBytes);

            assemblies = AppDomain.CurrentDomain.GetAssemblies();

            try
            {
                return assemblies.First(a => a.FullName == args.Name);
            }
            catch (Exception ex)
            {

                throw new CannotFindBindAssemblyException(ex);
            }
        }

    }
}